package labox.innovation.db.core;

import java.io.InputStream;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * A read/write HeapByteBuffer.
 */

public final class HeapByteBuffer extends InputStream {

	private static final ConcurrentLinkedQueue<HeapByteBuffer> _pool = new ConcurrentLinkedQueue<HeapByteBuffer>();

	protected byte buf[] = new byte[1024 * 10];

	protected int position;

	protected int limit;

	private HeapByteBuffer() {
		limit = buf.length;
		position = 0;
	}

	public static final HeapByteBuffer newInstance() {
		HeapByteBuffer buffer = _pool.poll();
		if (buffer == null)
			buffer = new HeapByteBuffer();
		return buffer;
	}

	public static final void recycle(HeapByteBuffer buffer) {
		if (buffer != null)
			_pool.offer(buffer);
	}

	public void clear() {
		position = 0;
		limit = buf.length;
	}

	public int position() {
		return position;
	}

	public int limit() {
		return limit;
	}

	public final HeapByteBuffer flip() {
		limit = position;
		position = 0;
		return this;
	}

	@Override
	public int read() {
		if (position >= limit) {
			throw new BufferUnderflowException();
		}
		return buf[position++] & 0xFF;
	}

	public int readByte() {
		return (byte) read();
	}

	public int readShort() {
		if (position + 1 >= limit) {
			throw new BufferUnderflowException();
		}
		return (buf[position++] & 0xff) << 8 | (buf[position++] & 0xff);
	}

	public int readInt() {
		if (position + 3 >= limit) {
			throw new BufferUnderflowException();
		}
		return (buf[position++] & 0xff) << 24 | (buf[position++] & 0xff) << 16 | (buf[position++] & 0xff) << 8 | (buf[position++] & 0xff);
	}

	public long readLong() {
		if (position + 7 >= limit) {
			throw new BufferUnderflowException();
		}
		return (buf[position++] & 0xffL) << 56 | (buf[position++] & 0xffL) << 48 | (buf[position++] & 0xffL) << 40 | (buf[position++] & 0xffL) << 32 | (buf[position++] & 0xffL) << 24 | (buf[position++] & 0xffL) << 16 | (buf[position++] & 0xffL) << 8 | (buf[position++] & 0xffL);
	}

	public String readString() {
		StringBuilder _sbuf = new StringBuilder();
		int len = readShort();
		if (len == -1 || position + (len << 1) > limit)
			return null;
		for (int i = -1; ++i < len;) {
			_sbuf.append((char) readShort());
		}
		return _sbuf.toString();
	}

	public float readFloat() {
		return Float.intBitsToFloat(readInt());
	}

	public double readDouble() {
		return Double.longBitsToDouble(readLong());
	}

	public int read(byte b[], int off, int len) {
		if (b == null) {
			throw new NullPointerException();
		} else if (off < 0 || len < 0 || len > b.length - off) {
			throw new IndexOutOfBoundsException();
		}

		if (position >= limit) {
			return -1;
		}
		if (position + len > limit) {
			len = limit - position;
		}
		if (len <= 0) {
			return 0;
		}
		System.arraycopy(buf, position, b, off, len);
		position += len;
		return len;
	}

	public void writeByte(int b) {
		if (position >= limit)
			throw new BufferOverflowException();
		buf[position++] = (byte) b;
	}

	public void writeShort(int b) {
		if (position + 1 >= limit)
			throw new BufferOverflowException();
		buf[position++] = (byte) (b >>> 8);
		buf[position++] = (byte) b;
	}

	public void writeInt(int b) {
		if (position + 3 >= limit) {
			throw new BufferOverflowException();
		}
		buf[position++] = (byte) (b >>> 24);
		buf[position++] = (byte) ((b >>> 16) & 0xff);
		buf[position++] = (byte) ((b >>> 8) & 0xff);
		buf[position++] = (byte) b;
	}

	public void writeLong(long b) {
		if (position + 7 >= limit)
			throw new BufferOverflowException();
		buf[position++] = (byte) (b >>> 56);
		buf[position++] = (byte) ((b >>> 48) & 0xff);
		buf[position++] = (byte) ((b >>> 40) & 0xff);
		buf[position++] = (byte) ((b >>> 32) & 0xff);
		buf[position++] = (byte) ((b >>> 24) & 0xff);
		buf[position++] = (byte) ((b >>> 16) & 0xff);
		buf[position++] = (byte) ((b >>> 8) & 0xff);
		buf[position++] = (byte) b;
	}

	public void writeString(String s) {
		int len = 0;
		if (s != null) {
			if (position + s.length() + 1 >= limit)
				throw new BufferOverflowException();
			len = s.length();
		}
		writeShort(len);
		for (int i = -1; ++i < len;) {
			writeShort((short) s.charAt(i));
		}
	}

	public void writeFloat(float b) {
		writeInt(Float.floatToIntBits(b));
	}

	public void writeDouble(double b) {
		writeLong(Double.doubleToLongBits(b));
	}

	public void write(byte b[], int off, int len) {
		if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) {
			throw new IndexOutOfBoundsException();
		} else if (len == 0) {
			return;
		}
		if (position + len - 1 >= limit)
			throw new BufferOverflowException();
		System.arraycopy(b, off, buf, position, len);
		position += len;
	}

	public int getPosition() {
		return position;
	}

	// public byte[] getBytes() {
	// return buf;
	// }
	public void setPosition(int position) {
		this.position = position;
	}

	public void writeHeapByteBuffer(HeapByteBuffer heapByteBuffer) {
		byte _buf[] = heapByteBuffer.buf;
		int _limit = heapByteBuffer.limit;
		if (_limit == 0) {
			return;
		}
		if (position + _limit - 1 >= limit)
			throw new BufferOverflowException();
		System.arraycopy(_buf, 0, buf, position, _limit);
		position += _limit;
	}

	public final int remaining() {
		return limit - position;
	}

	public final boolean hasRemaining() {
		return limit > position;
	}
}
